#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
A simple program to create and edit xml interface files for android midi pad in
a drag&drop fashion
"""

import wx

from wx.lib.intctrl import IntCtrl, EVT_INT

from xml.dom.minidom import Document, parse

ID_NEW = wx.NewId()
ID_OPEN = wx.NewId()
ID_EXPORT = wx.NewId()
ID_ABOUT = wx.NewId()
ID_EXIT = wx.NewId()

tools = ["Pad", "Fader H", "Fader V", "Button T", "Button M"]

templates = {
            "MAGIC/HERO/...":(480, 320),
            "DROID/NEXUS ONE/...":(800, 480),
            }

defaultSize = templates["MAGIC/HERO/..."]

#midi ranges setup
messageTypes = {"CC":0xB0, "PC":0xC0}
channels = range(0xF)
controls = range(0x7f) #0xxx xxxx



class Controller(object):
    def __init__(self, x, y, width, height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.type = None
        self.midiTypes = ["CC"]
        self.channels = [0]
        self.controlNumbers = [0]
        
    def draw(self, dc, viewType):
        dc.SetBrush(self.brush)
        dc.DrawRectangle(self.x, self.y, self.width, self.height)
        if viewType=="Type":
            dc.DrawText("%s%s"%(self.type[0], self.type[-1]), self.x+1, self.y+1)
        elif viewType=="CC/PC":
            if len(self.midiTypes)==1:
                dc.DrawText("%s"%self.midiTypes[0], self.x+1, self.y+1)
            elif len(self.midiTypes)==2:
                dc.DrawText("%s/%s"%(self.midiTypes[0], self.midiTypes[1]), self.x+1, self.y+1)
        elif viewType=="Channel/Control nb":
            if len(self.midiTypes)==1:
                dc.DrawText("%s/%s"%(self.channels[0],self.controlNumbers[0]), self.x+1, self.y+1)
            elif len(self.midiTypes)==2:
                dc.DrawText("%s/%s - %s/%s"%(self.channels[0],self.controlNumbers[0],
                                           self.channels[1],self.controlNumbers[1]),
                                    self.x+1, self.y+1)
    
    def testHit(self, x, y):
        return self.x<x and self.y<y and self.x+self.width>x and self.y+self.height>y
    
    def copyFrom(self, other):
        #copy the references, so that all can be updated at the same time,
        #good or not, it forces to group revelant controllers into the same channels (clean)
        self.width=other.width
        self.height=other.height
        self.midiTypes = other.midiTypes
        self.channels = other.channels
        if self.type=="Pad":
            self.controlNumbers = [p+2 for p in other.controlNumbers]
        else:
            self.controlNumbers = [p+1 for p in other.controlNumbers]

class Pad(Controller):
    def __init__(self, x=-1, y=-1, width=160, height=160):
        Controller.__init__(self, x, y, width, height)
        self.type = "Pad"
        self.brush = wx.Brush(wx.Colour(0, 0, 100), style=wx.TRANSPARENT)
        self.midiTypes.append("CC")
        self.channels.append(0)
        self.controlNumbers.append(1)

class FaderH(Controller):
    def __init__(self, x=-1, y=-1, width=160, height=40):
        Controller.__init__(self, x, y, width, height)
        self.type = "Fader H"
        self.brush = wx.Brush(wx.Colour(0, 100, 0), style=wx.TRANSPARENT)

class FaderV(Controller):
    def __init__(self, x=-1, y=-1, width=40, height=160):
        Controller.__init__(self, x, y, width, height)
        self.type = "Fader V"
        self.brush = wx.Brush(wx.Colour(100, 0, 0), style=wx.TRANSPARENT)

class ButtonT(Controller):
    def __init__(self, x=-1, y=-1, width=40, height=30):
        Controller.__init__(self, x, y, width, height)
        self.type = "Button T"
        self.brush = wx.Brush(wx.Colour(100, 0, 100), style=wx.TRANSPARENT)

class ButtonM(Controller):
    def __init__(self, x=-1, y=-1, width=40, height=30):
        Controller.__init__(self, x, y, width, height)
        self.type = "Button M"
        self.brush = wx.Brush(wx.Colour(100, 100, 100), style=wx.TRANSPARENT)

class ChooseSizeDlg(wx.Dialog):
    def __init__(self, parent, id, title):
        wx.Dialog.__init__(self, parent, id, title)

        vbox = wx.BoxSizer(wx.VERTICAL)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(wx.StaticText(self, -1, 'Template from phone model:'))
        self.modelCb = wx.ComboBox(self, -1, choices=templates.keys())
        hbox.Add(self.modelCb, 1)
        vbox.Add(hbox)
        
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(wx.StaticText(self, -1, 'custom width'))
        self.widthTxt=wx.TextCtrl(self, -1, '', (95, 105))
        hbox.Add(self.widthTxt)
        hbox.Add(wx.StaticText(self, -1, 'height'))
        self.heightTxt=wx.TextCtrl(self, -1, '', (95, 105))
        hbox.Add(self.heightTxt)
        vbox.Add(hbox, 1, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 10)
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        okButton = wx.Button(self, wx.ID_OK, 'Ok', size=(70, 30))
        cancelButton = wx.Button(self, wx.ID_CANCEL, 'Cancel', size=(70, 30))
        hbox.Add(okButton, 1)
        hbox.Add(cancelButton, 1, wx.LEFT, 5)
        
        vbox.Add(hbox, 1, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 10)
        
        self.SetSizerAndFit(vbox)

    def getResults(self):
        if self.modelCb.GetSelection()==wx.NOT_FOUND:
            width = self.widthTxt.GetLabel()
            height = self.heightTxt.GetLabel()
            return (int(width), int(height))
        else:
            return templates[templates.keys()[self.modelCb.GetSelection()]]
    
    

class ToolPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1, size=(100, -1))
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.btnTools = []
        self.selectedTool = None
        for tool in tools:
            btn = wx.ToggleButton(self, -1, tool)
            self.Bind(wx.EVT_TOGGLEBUTTON, self.ToolToggled, btn)
            self.btnTools.append(btn)
            sizer.Add(btn, flag=wx.EXPAND)
        
        self.viewTypeBox = wx.RadioBox(self, -1, label="Display",
                                       choices=["Type", "CC/PC", "Channel/Control nb"],
                                       style=wx.RA_VERTICAL)
        self.Bind(wx.EVT_RADIOBOX, parent.OnChangeViewType, self.viewTypeBox)
        sizer.Add(self.viewTypeBox, flag = wx.EXPAND|wx.ALL)
        
        self.parent = parent
        self.SetSizer(sizer)

    def ToolToggled(self, evt):
        tool = evt.GetEventObject()
        if not tool.GetValue():
            self.selectedTool = None
        else:
            for toolBtn in self.btnTools:
                if toolBtn.GetValue() and toolBtn!=tool:
                    toolBtn.SetValue(False)
                self.selectedTool = tools[self.btnTools.index(tool)]
        self.parent.setTempTool(self.selectedTool)
        evt.Skip()
    
    def Reset(self):
        for toolBtn in self.btnTools:
            toolBtn.SetValue(False)    
    

class PropertiesPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1, size=(200, -1))
        sizer = wx.BoxSizer(wx.VERTICAL)
        grid = wx.GridSizer(5,2)
        self.parent = parent
        
        grid.Add(wx.StaticText(self, -1, "Controller : "))
        self.controllerTypeTxt = wx.StaticText(self, -1, "Nothing Selected")
        grid.Add(self.controllerTypeTxt)
        
        grid.Add(wx.StaticText(self, -1, "x position : "))
        self.xTxt = IntCtrl(self, -1, 0, min=0, limited=True, allow_none=False)
        grid.Add(self.xTxt)
        
        grid.Add(wx.StaticText(self, -1, "y position : "))
        self.yTxt = IntCtrl(self, -1, 0, min=0, limited=True, allow_none=False)
        grid.Add(self.yTxt)
        
        grid.Add(wx.StaticText(self, -1, "width : "))
        self.widthTxt = IntCtrl(self, -1, 0, min=0, limited=True, allow_none=False)
        grid.Add(self.widthTxt)
        
        grid.Add(wx.StaticText(self, -1, "height : "))
        self.heightTxt = IntCtrl(self, -1, 0, min=0, limited=True, allow_none=False)
        grid.Add(self.heightTxt)
        
        sizer.Add(grid, flag=wx.EXPAND)
        
        sb = wx.StaticBox(self, -1, "Midi messages")
        sbs = wx.StaticBoxSizer(sb)
        grid = wx.GridSizer(0,2)
        
        grid.Add(wx.StaticText(self, -1, "Message 1"))
        grid.Add(wx.StaticText(self, -1, ""))
        
        grid.Add(wx.StaticText(self, -1, "Message type:"))
        self.midiType1 = wx.ComboBox(self, -1, choices=messageTypes.keys())
        self.midiType1.SetSelection(0)
        grid.Add(self.midiType1)
        
        grid.Add(wx.StaticText(self, -1, "channel : "))
        self.channel1 = IntCtrl(self, -1, 0, min=0, max=127, limited=True, allow_none=False)
        grid.Add(self.channel1)
        
        grid.Add(wx.StaticText(self, -1, "control number : "))
        self.controlNumber1 = IntCtrl(self, -1, 0, min=0, max=127, limited=True, allow_none=False)
        grid.Add(self.controlNumber1)
        
        grid.Add(wx.StaticText(self, -1, "Message 2"))
        grid.Add(wx.StaticText(self, -1, "(only for the pad)"))
        
        grid.Add(wx.StaticText(self, -1, "Message type:"))
        self.midiType2 = wx.ComboBox(self, -1, choices=messageTypes.keys())
        self.midiType2.SetSelection(0)
        grid.Add(self.midiType2)
        
        grid.Add(wx.StaticText(self, -1, "channel : "))
        self.channel2 = IntCtrl(self, -1, 0, min=0, max=127, limited=True, allow_none=False)
        grid.Add(self.channel2)
        
        grid.Add(wx.StaticText(self, -1, "control number : "))
        self.controlNumber2 = IntCtrl(self, -1, 0, min=0, max=127, limited=True, allow_none=False)
        grid.Add(self.controlNumber2)
        
        sbs.Add(grid)
        sizer.Add(sbs, flag=wx.EXPAND)
        
        self.btnCommit = wx.Button(self, -1, "Commit modifications")
        self.Bind(wx.EVT_BUTTON, self.saveModifs, self.btnCommit)
        sizer.Add(self.btnCommit, flag=wx.EXPAND)
        
        self.SetSizer(sizer)
    
    def saveModifs(self, evt):
        self.parent.updateCurrentController()
        evt.Skip()
    
    def updateProperties(self, controller):
        if controller==None:
            self.controllerTypeTxt.SetLabel("Nothing selected")
            self.xTxt.SetLabel("0")
            self.yTxt.SetLabel("0")
            self.widthTxt.SetLabel("0")
            self.heightTxt.SetLabel("0")
            self.midiType1.SetStringSelection("CC")
            self.channel1.SetLabel("0")
            self.controlNumber1.SetLabel("0")
            self.midiType2.SetStringSelection("CC")
            self.channel2.SetLabel("0")
            self.controlNumber2.SetLabel("0")
        else:
            self.controllerTypeTxt.SetLabel(controller.type)
            self.xTxt.SetLabel(str(controller.x))
            self.yTxt.SetLabel(str(controller.y))
            self.widthTxt.SetLabel(str(controller.width))
            self.heightTxt.SetLabel(str(controller.height))
            self.midiType1.SetStringSelection(controller.midiTypes[0])
            self.channel1.SetLabel(str(controller.channels[0]))
            self.controlNumber1.SetLabel(str(controller.controlNumbers[0]))
            if controller.type=="Pad":
                self.midiType2.Enable(True)
                self.channel2.Enable(True)
                self.controlNumber2.Enable(True)
                self.midiType2.SetStringSelection(controller.midiTypes[1])
                self.channel2.SetLabel(str(controller.channels[1]))
                self.controlNumber2.SetLabel(str(controller.controlNumbers[1]))
            else:
                self.midiType2.Enable(False)
                self.channel2.Enable(False)
                self.controlNumber2.Enable(False)
        
        
class InterfaceBuilder(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, size=(800,600),
                          title="Android midi pad - interface builder",
                          style=wx.MINIMIZE_BOX|wx.CLOSE_BOX|wx.SYSTEM_MENU|wx.CAPTION)
        menu = wx.Menu()
        menu.Append(ID_NEW, "&New\tCtrl+N")
        menu.Append(ID_OPEN, "&Open\tCtrl+O")
        menu.Append(ID_EXPORT, "&Save/Export\tCtrl+S")
        menu.AppendSeparator()
        menu.Append(ID_ABOUT, "&About\tF1")
        menu.Append(ID_EXIT, "&Exit")
        menuBar = wx.MenuBar()
        menuBar.Append(menu, "File")
        self.SetMenuBar(menuBar)
        
        self.Bind(wx.EVT_MENU, self.OnNew, id=ID_NEW)
        self.Bind(wx.EVT_MENU, self.OnExport, id=ID_EXPORT)
        self.Bind(wx.EVT_MENU, self.OnOpen, id=ID_OPEN)
        self.Bind(wx.EVT_MENU, self.OnAbout, id=ID_ABOUT)
        self.Bind(wx.EVT_MENU, self.OnExit, id=ID_EXIT)
        
        self.controllers = []
        self.tempTool = None #when not yet fixed on the grid
        self.selectedTool = None
        self.gridSize = 4
        
        
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.toolPanel = ToolPanel(self)
        sizer.Add(self.toolPanel, flag=wx.EXPAND)
        
        self.canvas = wx.Panel(self, -1, size=defaultSize)
        self.canvas.SetDoubleBuffered(True)
        self.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown, self.canvas)
        self.canvas.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown, self.canvas)
        self.canvas.Bind(wx.EVT_LEFT_UP, self.OnLeftUp, self.canvas)
        self.canvas.Bind(wx.EVT_MOTION, self.OnMove, self.canvas)
        self.canvas.Bind(wx.EVT_PAINT, self.OnPaint, self.canvas)
        sizer.Add(self.canvas)
        
        #handle duplicate and delete simply
        self.canvas.Bind(wx.EVT_CHAR, self.OnKeyDown, self.canvas)
        
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBG)
        
        self.propertiesPanel = PropertiesPanel(self)
        sizer.Add(self.propertiesPanel, flag=wx.EXPAND)
        
        self.SetSizerAndFit(sizer)
        
        self.Show(True)
        
        self.viewType = "Type"
    
    #def OnNew(self, evt):
    #    dlg = wx.MessageDialog(self, caption="Confirmation", message="You will loose all changes made since the last save, are you sure?")
    #    if dlg.ShowModal()==wx.ID_OK:
    #        del self.controllers[:]
    #        self.canvas.Refresh()
    #        self.propertiesPanel.updateProperties(None)
    #    dlg.Destroy()
        
    #TODO:
    def OnNew(self, evt):
        dlg = ChooseSizeDlg(self, -1, "Create a new interface")
        if dlg.ShowModal()==wx.ID_OK:
            width, height = dlg.getResults()
            import pdb
            pdb.set_trace()
            print "setting canvas size to:", width, height
            self.canvas.SetSize((width, height))
            print "canvas size :", self.canvas.Size
            del self.controllers[:]
            self.canvas.Refresh()
            self.propertiesPanel.updateProperties(None)
            self.Fit()
        dlg.Destroy()
    
    #compare the python code to the java code, and cry...
    def OnOpen(self, evt):
        dialog = wx.FileDialog(self, style = wx.OPEN)
        dialog.SetWildcard("Midi pad xml files (*.xml)|*.xml")
        if dialog.ShowModal() == wx.ID_OK:
            #reset program state
            del self.controllers[:]
            self.canvas.Refresh()
            self.propertiesPanel.updateProperties(None)
            #parse xml
            filePath = dialog.GetPath()
            doc = parse(filePath)
            controllers = doc.getElementsByTagName("controller")
            for cn in controllers:
                type = cn.getAttribute("type").lower()
                c = None
                if type=="pad":
                    c = Pad()
                elif type=="fader h":
                    c = FaderH()
                elif type=="fader v":
                    c = FaderV()
                elif type=="button t":
                    c = ButtonT()
                elif type=="button m":
                    c = ButtonM()
                c.x = int(cn.getAttribute("x"))
                c.y = int(cn.getAttribute("y"))
                c.width = int(cn.getAttribute("width"))
                c.height = int(cn.getAttribute("height"))
                c.midiTypes = []
                c.channels = []
                c.controlNumbers = []
                midiMessageNodes = cn.getElementsByTagName("midiMessage")
                for m in midiMessageNodes:
                    ccpc = "CC" if int(m.getAttribute("statusByte"))&0xf0==messageTypes["CC"] else "PC"
                    c.midiTypes.append(ccpc)
                    c.channels.append(int(m.getAttribute("statusByte"))&0x0f)
                    c.controlNumbers.append(int(m.getAttribute("data1Byte")))
                self.controllers.append(c)
            self.canvas.Refresh()
        dialog.Destroy()
        #evt.Skip()
    
    def OnExport(self, evt):
        dialog = wx.FileDialog(self, style = wx.SAVE|wx.FD_OVERWRITE_PROMPT)
        dialog.SetWildcard("Midi pad xml files (*.xml)|*.xml")
        if dialog.ShowModal() == wx.ID_OK:
            filePath = dialog.GetPath()
            doc = Document()
            root = doc.createElement("midipad")
            root.setAttribute("width", "480")
            root.setAttribute("height", "320")
            doc.appendChild(root)
            controllersNode = doc.createElement("controllers")
            root.appendChild(controllersNode)
            for c in self.controllers:
                node = doc.createElement("controller")
                node.setAttribute("type", c.type)
                node.setAttribute("x", str(c.x))
                node.setAttribute("y", str(c.y))
                node.setAttribute("width", str(c.width))
                node.setAttribute("height", str(c.height))
                midiNode = doc.createElement("midiMessages")
                node.appendChild(midiNode)
                for ct, cc, cn in zip(c.midiTypes, c.channels, c.controlNumbers):
                    msgNode = doc.createElement("midiMessage")
                    msgNode.setAttribute("statusByte", str(messageTypes[ct]+cc))
                    msgNode.setAttribute("data1Byte", str(cn))
                    midiNode.appendChild(msgNode)
                controllersNode.appendChild(node)
            with open(filePath, "w") as f:
                f.write(doc.toprettyxml())
        dialog.Destroy()
    
    def OnExit(self, evt):
        self.Destroy()
    
    def OnAbout(self, evt):
        description = """MidiPad - A midi event emitter for android devices
through a bluetooth connection.
XML Interface builder software."""
        licence = """
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
        info = wx.AboutDialogInfo()
        info.SetIcon(wx.Icon('images/icon.png', wx.BITMAP_TYPE_PNG))
        info.SetName('AndroidMidiPad interface builder')
        info.SetVersion('1.0')
        info.SetDescription(description)
        info.SetCopyright('(C) 2009-2010 Timothe Faudot')
        info.SetWebSite('http://code.google.com/p/androidmidipad/')
        info.SetLicence(licence)
        #info.AddDeveloper('')
        #info.AddDocWriter('')
        #info.AddArtist('')
        #info.AddTranslator('')
        wx.AboutBox(info)

    #do nothing because we use a BufferedDC
    def OnEraseBG(self, evt):
        pass
    
    def OnPaint(self, evt):
        dc = wx.PaintDC(self.canvas)
        self.DoDraw(dc)
        evt.Skip()
    
    def DoDraw(self, dc):
        step = self.gridSize #point every 4px
        #width, height = dc.GetSizeTuple()
        #for i in xrange(0, width, step):
        #    for j in xrange(0, height, step):
        #        dc.DrawCircle(i, j, 1)
        for controller in self.controllers:
            controller.draw(dc, self.viewType)
        if self.tempTool:
            self.tempTool.draw(dc, self.viewType)
    
    def OnKeyDown(self, evt):
        k = evt.GetKeyCode()
        if k==wx.WXK_DELETE:
            if self.selectedTool:
                self.controllers.remove(self.selectedTool)
                self.canvas.Refresh()
                self.propertiesPanel.updateProperties(None)
                self.selectedTool = None
        evt.Skip()
    
    def OnRightDown(self, evt):
        x, y = evt.GetPosition()
        if self.tempTool==None:
            found=False
            for controller in self.controllers:
                if controller.testHit(x, y):
                    found=True
                    break
            if found:
                if self.selectedTool.type=="Pad":
                    self.tempTool = Pad()
                elif self.selectedTool.type=="Fader H":
                    self.tempTool = FaderH()
                elif self.selectedTool.type=="Fader V":
                    self.tempTool = FaderV()
                elif self.selectedTool.type=="Button T":
                    self.tempTool = ButtonT()
                elif self.selectedTool.type=="Button M":
                    self.tempTool = ButtonM()
                self.tempTool.copyFrom(self.selectedTool)
                self.selectedTool = None
                self.propertiesPanel.updateProperties(None)
    
    def OnLeftDown(self, evt):
        x, y = evt.GetPosition()
        if self.tempTool!=None:
            self.controllers.append(self.tempTool)
            self.tempTool = None
            self.toolPanel.Reset()
            self.canvas.Refresh()
            self.propertiesPanel.updateProperties(self.controllers[-1])
            self.selectedTool = self.controllers[-1]
        else:
            found=False
            for controller in self.controllers:
                if controller.testHit(x, y):
                    found=True
                    break
            if not found:
                    self.propertiesPanel.updateProperties(None)
                    self.selectedTool = None
            else:
                self.selectedTool = controller
                self.propertiesPanel.updateProperties(controller)
        evt.Skip()
    
    def OnLeftUp(self, evt):
        evt.Skip()
    
    def OnMove(self, evt):
        if self.tempTool:
            width, height = self.canvas.GetSize()
            x, y = evt.GetPosition()
            if x+self.tempTool.width>width:
                x=width-self.tempTool.width
            if y+self.tempTool.height>height:
                y=height-self.tempTool.height
            self.tempTool.x = x-(x%self.gridSize)
            self.tempTool.y = y-(y%self.gridSize)
            self.canvas.Refresh()
        if self.selectedTool and evt.LeftIsDown():
            width, height = self.canvas.GetSize()
            x, y = evt.GetPosition()
            if x+self.selectedTool.width>width:
                x=width-self.selectedTool.width
            if y+self.selectedTool.height>height:
                y=height-self.selectedTool.height
            self.selectedTool.x = x-(x%self.gridSize)
            self.selectedTool.y = y-(y%self.gridSize)
            self.propertiesPanel.updateProperties(self.selectedTool)
            self.canvas.Refresh()
        evt.Skip()
    
    def setTempTool(self, tool):
        if tool==None:
            self.tempTool = None
            self.canvas.Refresh()
        else:
            if tool=="Pad":
                self.tempTool = Pad()
            elif tool=="Fader H":
                self.tempTool = FaderH()
            elif tool=="Fader V":
                self.tempTool = FaderV()
            elif tool=="Button T":
                self.tempTool = ButtonT()
            elif tool=="Button M":
                self.tempTool = ButtonM()
            #self.propertiesPanel.updateProperties(self.tempTool)

    def updateCurrentController(self):
        #called when the commit modifs button is pressed
        t = self.selectedTool
        p = self.propertiesPanel
        if t:
            t.x = int(p.xTxt.GetLabel())
            t.y = int(p.yTxt.GetLabel())
            t.width = int(p.widthTxt.GetLabel())
            t.height = int(p.heightTxt.GetLabel())
            t.midiTypes[0] = p.midiType1.GetStringSelection()
            t.channels[0] = int(p.channel1.GetLabel())
            t.controlNumbers[0] = int(p.controlNumber1.GetLabel())
            if t.type=="Pad":
                t.midiTypes[1] = p.midiType2.GetStringSelection()
                t.channels[1] = int(p.channel2.GetLabel())
                t.controlNumbers[1] = int(p.controlNumber2.GetLabel())
            self.canvas.Refresh()
    
    def OnChangeViewType(self, evt):
        self.viewType = evt.GetEventObject().GetStringSelection()
        self.canvas.Refresh()
    
if __name__=="__main__":
    app = wx.App()
    app.RestoreStdio()
    InterfaceBuilder()
    app.MainLoop()